-- 2022-8-17

--[=[ 18.4 按顺序遍历表

    一个常见的困惑发生在开发人员想要对表中的元素进行排序时
    由于一个表中的元素没有顺序 所以如果想对这些元素排序 就不得不先把键值对拷贝到一个数组中 然后再对数组进行排序

    我们在第11章"小插曲:出现频率最高的单词"项目中已经看到过这个技巧的例子 这里 让我们再举一个例子
    假设我们要读取一个源文件 然后构造一个表来保存每个函数的名称及其声明所在的行数 形式如下:
    lines = {
        ["luaH_set"] = 10,
        ["luaH_get"] = 24,
        ["luaH_present"] = 48,
    }
    现在 我们想按照字母顺序输出这些函数名 如果使用pairs遍历表 那么函数名会按照随机的顺序出现
    由于这些函数名是表的键 所以我们无法直接对其进行排序 不过 如果 我们把它们放到数组中 那么就可以对它们进行排序了
    首先 我们必须创建一个包含函数名的数组 然后对其排序 再最终输出结果
    a = {}
    for n in pairs(lines) do a[#a + 1] = n end
    table.sort(a)
    for _,n in ipairs(a) do print(n) end
    有些人可能会困惑 毕竟 对于Lua语言来说 数组也没有顺序(毕竟它们是表) 但是我们知道如何数数
    因此 当我们使用有序的索引访问数组时 就实现了有序 这正是应该总是使用ipairs而不是pairs来遍历数组的原因
    第一个函数通过有序的键1 2等来实现有序 然后后者使用的则是天然的随机顺序(虽然大多数情况下顺序随机也无碍 但有时可能并非我们想要的)

    现在 我们已经准备好写一个按照键的顺序来遍历表的迭代器了:
    function pairsByKeys(t,f)
        local a = {}
        for n in pairs(t) do
            a[#a + 1] = n
        end
        table.sort(a,f)
        local i = 0
        return function ()
            i = i + 1
            return a[i],t[a[i]]
        end
    end
    工厂函数pairsByKeys首先把键放到一个数组中 然后对数组进行排序 最后返回迭代函数
    在每一步中 迭代器都会按照数组a中的顺序返回原始表中的下一个键值对 可选的参数f允许指定一种其他的排序方式

    使用这个函数 可以很容易地解决开始时提出的按顺序遍历表的问题:
    for name,line in pairsByKeys(lines) do
        print(name,line)
    end
    像通常的情况一样 所有的复杂性都被隐藏到了迭代器中
]=]